package org.openfantasia.musica.instruments.jmusic;

import jm.audio.AudioObject;
import jm.audio.io.SampleIn;
import jm.audio.io.SampleOut;
import jm.audio.synth.*;

public final class SubtractiveSampleInst extends jm.audio.Instrument {
	//----------------------------------------------
	// Attributes
	//----------------------------------------------

	/**
	 * the name of the sample file
	 */
	private String fileName;

	/**
	 * How many channels is the sound file we are using
	 */
	private int numOfChannels;

	/**
	 * the base frequency of the sample file to be read in
	 */
	private double baseFreq;

	/**
	 * should we play the wholeFile or just what we need for note duration
	 */
	private boolean wholeFile;

	/**
	 * The envelope sustain value
	 */
	private double sustain;

	/**
	 * low pass filter cutoff
	 */
	private int cutoff;

	/**
	 * Envelope attributes
	 */
	private int attack, decay, release;

	/**
	 * Depth of filter modulation (0.0 - 1.0)
	 */
	private double modAmount = 0.9;

	/**
	 * Speed of filter modulation in Hertz
	 */
	private double modRate = 0.05;

	/**
	 * The volume of the squarewave sub oscillator
	 */
	private double subAmp = 0.0;
	//----------------------------------------------
	// Constructor
	//----------------------------------------------


	/**
	 * Constructor
	 */
	public SubtractiveSampleInst(String fileName) {
		this(fileName, 440.00, 500);
	}


	public SubtractiveSampleInst(String fileName, double baseFreq) {
		this(fileName, baseFreq, 500);
	}


	public SubtractiveSampleInst(String fileName, double baseFreq, int cutoff) {
		this(fileName, baseFreq, cutoff, 2, 50, 0.4, 200);
	}


	public SubtractiveSampleInst(String fileName, double baseFreq, int cutoff, int attack, int decay, double sustain, int release) {
		this.fileName = fileName;
		this.baseFreq = baseFreq;
		this.cutoff = cutoff;
		this.attack = attack;
		this.decay = decay;
		this.release = release;
		this.sustain = sustain;
	}

	//----------------------------------------------
	// Methods
	//----------------------------------------------


	public void setModAmount(double val) {
		this.modAmount = val;
	}


	public void setModDepth(double val) {
		this.modAmount = val;
	}


	public void setModRate(double val) {
		this.modRate = val;
	}


	public void setWholeFile(boolean val) {
		this.wholeFile = val;
	}


	public void setSubAmp(double val) {
		this.subAmp = val;
	}


	/**
	 * Create the Audio Chain for this Instrument
	 * and assign the primary Audio Object(s). The
	 * primary audio object(s) are the one or more
	 * objects which head up the chain(s)
	 */
	public void createChain() {
		//define the chain
		SampleIn sin = new SampleIn(this, fileName);
		sin.setWholeFile(wholeFile);
		ReSample reSample = new ReSample(sin, this.baseFreq);
		// modulate filter cutoff
		Oscillator sineMod = new Oscillator(this, Oscillator.SINE_WAVE, sin.getSampleRate(),
				sin.getChannels(), Oscillator.FREQUENCY, (float) modRate);
		sineMod.setAmp((float) this.modAmount * this.cutoff);
		// sub
		Oscillator subOsc = new Oscillator(this, Oscillator.SQUARE_WAVE, sin.getSampleRate(),
				sin.getChannels());
		subOsc.setFrqRatio(0.5f);
		subOsc.setAmp((float) subAmp);
		Add adder = new Add(new AudioObject[]{reSample, subOsc});
		// filter
		Filter filt = new Filter(new AudioObject[]{adder, sineMod}, this.cutoff, Filter.LOW_PASS);
		ADSR env = new ADSR(filt, attack, decay, sustain, release);
		Volume vol = new Volume(env);
		StereoPan span = new StereoPan(vol);
		SampleOut sout = new SampleOut(span);
	}
}
